// Copyright (c) 2023 CHANGLEI. All rights reserved.

import 'package:flutter/services.dart';

/// Created by changlei on 2020/05/26
///
/// 输入有效的小数
abstract final class DecimalTextInputFormatter extends FilteringTextInputFormatter {
  /// 输入有效的小数
  factory DecimalTextInputFormatter({
    int decimalDigits = 2,
    bool isNegative = false,
    double? maxValue,
    double? minValue,
    ValueChanged<double>? onOutOfRange,
  }) {
    if (isNegative) {
      return _NegativeDecimalTextInputFormatter(
        decimalDigits: decimalDigits,
        maxValue: maxValue,
        minValue: minValue,
        onOutOfRange: onOutOfRange,
      );
    } else {
      return _PositiveDecimalTextInputFormatter(
        decimalDigits: decimalDigits,
        maxValue: maxValue,
        minValue: minValue,
        onOutOfRange: onOutOfRange,
      );
    }
  }

  DecimalTextInputFormatter._(super.filterPattern) : super.allow();

  /// 此参数等于0，相当于只能输入整数，等于-1，相当于不限制小数位数，默认等于2
  int get decimalDigits;

  /// 最大值，不设置的时候相当于不限制最大值
  double? get maxValue;

  /// 最小值，不设置的时候相当于不限制最小值
  double? get minValue;

  /// 超过最大值回调
  ValueChanged<double>? get onOutOfRange;

  RegExp? get _decimalDigitsRegExp;

  String _parse(String beforePointer);

  @override
  TextEditingValue formatEditUpdate(TextEditingValue oldValue, TextEditingValue newValue) {
    final editingValue = super.formatEditUpdate(oldValue, newValue);
    var newValueText = editingValue.text;
    if (newValueText.isEmpty) {
      return editingValue;
    }
    final pointerIndex = newValueText.indexOf('.');
    final length = newValueText.length;
    final beforePointer = newValueText.substring(0, pointerIndex == -1 ? length : pointerIndex);
    final parsed = _parse(beforePointer);
    if (_decimalDigitsRegExp != null) {
      final matches = _decimalDigitsRegExp!.allMatches(newValueText);
      final groups = matches.map((e) => e.group(0)).whereType<String>().toList();
      groups.sort((a, b) => b.length.compareTo(a.length));
      newValueText = groups.firstOrNull ?? newValueText;
    }
    newValueText = newValueText.replaceFirst(beforePointer, parsed);
    var offset = editingValue.selection.baseOffset;
    if (parsed != beforePointer) {
      offset -= beforePointer.length - parsed.length;
    }
    final num = double.tryParse(newValueText);
    if (maxValue != null && num != null && num > maxValue!) {
      onOutOfRange?.call(num);
      return oldValue;
    }
    if (minValue != null && num != null && num < minValue!) {
      onOutOfRange?.call(num);
      return oldValue;
    }
    return editingValue.copyWith(
      text: newValueText,
      selection: TextSelection.collapsed(
        offset: offset.clamp(-1, newValueText.length),
      ),
    );
  }
}

final class _PositiveDecimalTextInputFormatter extends DecimalTextInputFormatter {
  /// 输入有效的小数
  _PositiveDecimalTextInputFormatter({
    this.decimalDigits = 2,
    this.maxValue,
    this.minValue,
    this.onOutOfRange,
  })  : assert(decimalDigits >= 0 || decimalDigits == -1),
        assert(maxValue == null || minValue == null || maxValue >= minValue),
        _decimalDigitsRegExp = decimalDigits < 0 ? null : RegExp('\\d+\\.?\\d{0,$decimalDigits}'),
        super._(RegExp(decimalDigits == 0 ? r'\d+' : r'\d+\.?\d*'));

  @override
  final int decimalDigits;

  @override
  final double? maxValue;

  @override
  final double? minValue;

  @override
  final ValueChanged<double>? onOutOfRange;

  @override
  final RegExp? _decimalDigitsRegExp;

  @override
  String _parse(String beforePointer) {
    var parsed = beforePointer.replaceFirst(RegExp(r'^0+'), '');
    if (parsed.isEmpty && beforePointer.isNotEmpty) {
      parsed = '0';
    }
    return parsed;
  }
}

/// 可以设置最大值和负数
final class _NegativeDecimalTextInputFormatter extends DecimalTextInputFormatter {
  /// 可以设置最大值和负数
  _NegativeDecimalTextInputFormatter({
    this.decimalDigits = 2,
    this.maxValue,
    this.minValue,
    this.onOutOfRange,
  })  : assert(decimalDigits >= 0 || decimalDigits == -1),
        assert(maxValue == null || minValue == null || maxValue >= minValue),
        _decimalDigitsRegExp = decimalDigits < 0 ? null : RegExp('-?\\d*\\.?\\d{0,$decimalDigits}'),
        super._(RegExp(decimalDigits == 0 ? r'-?\d*' : r'-?(\d+\.)?\d*'));

  @override
  final int decimalDigits;

  @override
  final double? maxValue;

  @override
  final double? minValue;

  @override
  final ValueChanged<double>? onOutOfRange;

  @override
  final RegExp? _decimalDigitsRegExp;

  @override
  String _parse(String beforePointer) {
    var parsed = beforePointer.replaceFirst(RegExp(r'^-?0+'), '');
    if (parsed.isEmpty && beforePointer.isNotEmpty) {
      parsed = '0';
    }
    if (!parsed.startsWith('-') && beforePointer.startsWith('-')) {
      parsed = '-$parsed';
    }
    return parsed;
  }

  @override
  TextEditingValue formatEditUpdate(TextEditingValue oldValue, TextEditingValue newValue) {
    return super.formatEditUpdate(
      oldValue,
      newValue.copyWith(
        text: newValue.text.replaceAll(RegExp(r'[–—]'), '-'),
      ),
    );
  }
}
